//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "pcalc.h"
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <Clipbrd.hpp>
#include <IniFiles.hpp>
#include "about.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
Tpcfrm *pcfrm;

char symbols[37] = "0123456789ABCDEFGHIJKLMNoPQRSTUVWXYZ";
const int MAX_BASE = 36;


//---------------------------------------------------------------------------
__fastcall Tpcfrm::Tpcfrm(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
AnsiString Tpcfrm::makeitfancy(int base, AnsiString s)
{
// make it fancy

AnsiString s1,s2;
int n;

    if (!SeparatorsCB->Checked) return s;

    if ((base==2) & (s.Length()>4)) {
      s1="";
      for (n=s.Length(); n>=0; n--)
      {
        if (((s.Length()-n-1)%4 == 0) && (n<s.Length()-3)) s1=" "+s1;
        s2 =  s.c_str()[n];
        s1 =  s2 + s1;
      }
      return s1;
    }

    if ((base==16) & (s.Length()>4)) {
      s1="";
      for (n=s.Length(); n>=0; n--)
      {
        if (((s.Length()-n-1)%4 == 0) && (n<s.Length()-3)) s1=":"+s1;
        s2 =  s.c_str()[n];
        s1 =  s2 + s1;
      }
      return s1;
    }


    if ((base==10) & (s.Length()>3)) {
      s1="";
      for (n=s.Length(); n>=0; n--)
      {
        if (((s.Length()-n-1)%3 == 0) && (n<s.Length()-3)) s1="."+s1;
        s2 =  s.c_str()[n];
        s1 =  s2 + s1;
      }
      return s1;
    }


    return s;


}


void __fastcall Tpcfrm::Button1Click(TObject *Sender)
{
  int i,j,n;
  bool err;
  AnsiString s,s1,s2;


  cs1 = Edit1->Text;
  bs1 = EditB1->Text;
  bs2 = EditB2->Text;
  bs3 = EditB3->Text;
  i = StrToInt(EditB1->Text);
  j = StrToInt(EditB2->Text);
  s = Convert(i, j, Edit1->Text, &err);
  cs2 = s;
  if (!err) s = makeitfancy(j, s);
  Label2->Caption = s;

  i = StrToInt(EditB1->Text);
  j = StrToInt(EditB3->Text);
  if (err) Label3->Caption = "Error.";
  else
  {
    s = Convert(i, j, Edit1->Text, &err);
    cs3=s;
    if (!err) s = makeitfancy(j, s);
    Label3->Caption = s;

  }

  if (Extended1->Checked) UpdateNotation();

  INI_write();
  

}
//---------------------------------------------------------------------------
AnsiString Tpcfrm::Convert(int from, int to, AnsiString s, bool *error)
{
 //Return error if input is empty
            *error = false;
            if (s.Length() == 0)
            {
                *error = true;
                return ("Error: Nothing in Input String");
               
            }
            //only allow uppercase input characters in string
            s = s.UpperCase();

            //only do base 2 to base 36 (digit represented by charecaters 0-Z)"
            if (from < 2 || from > 36 || to < 2 || to > 36) { return ("Base requested outside range"); }

           //convert string to an array of integer digits representing number in base:from
            int il = s.Length();
            int *fs = new int[il];
            int k = 0;
            for (int i = s.Length() - 1; i >= 0; i--)
            {
                if (s.c_str()[i] >= '0' && s.c_str()[i] <= '9') { fs[k++] = (int)(s.c_str()[i] - '0'); }
                else
                {
                    if (s.c_str()[i] >= 'A' && s.c_str()[i] <= 'Z') { fs[k++] = 10 + (int)(s.c_str()[i] - 'A'); }
                    else
                    {  delete fs;
                    *error = true;
                       return ("Error: Input string must only contain any of 0-9 or A-Z"); } //only allow 0-9 A-Z characters
                }
            }

            //check the input for digits that exceed the allowable for base:from
            for (int i=0; i<il; i++)
            {
                if (fs[i] >= from) {
                  delete fs;
                  *error = true;
                  return ("Error: Not a valid number for this input base"); }
            }

            //find how many digits the output needs
            int ol = il * (from / to+1);
            int *ts = new int[ol+10]; //assign accumulation array
            int *cums = new int[ol+10]; //assign the result array
            for (int nnn=0; nnn<ol+10; nnn++) { ts[nnn] = 0; cums[nnn] = 0; }
            ts[0] = 1; //initialise array with number 1

            //evaluate the output
            for (int i = 0; i < il; i++) //for each input digit
            {
                for (int j = 0; j < ol; j++) //add the input digit times (base:to from^i) to the output cumulator
                {
                    cums[j] += ts[j] * fs[i];
                    int temp = cums[j];
                    int rem = 0;
                    int ip = j;
                    do // fix up any remainders in base:to
                    {
                        rem = temp / to;
                        cums[ip] = temp-rem*to; ip++;
                        cums[ip] += rem;
                        temp = cums[ip];
                    }
                    while (temp >=to);
                }

                //calculate the next power from^i) in base:to format
                for (int j = 0; j < ol; j++)
                {
                    ts[j] = ts[j] * from;
                } 
                for(int j=0;j<ol;j++) //check for any remainders
                {
                    int temp = ts[j];
                    int rem = 0;
                    int ip = j;
                    do  //fix up any remainders
                    {
                        rem = temp / to;
                        ts[ip] = temp - rem * to; ip++;
                        ts[ip] += rem;
                        temp = ts[ip];
                    }
                    while (temp >= to);
                }
            }

            //convert the output to string format (digits 0,to-1 converted to 0-Z characters) 
            AnsiString sout;// = String.Empty; //initialise output string
            bool first = false; //leading zero flag
            for (int i = ol ; i >= 0; i--)
            {
                if (cums[i] != 0) { first = true; }
                if (!first) { continue; }
                if (cums[i] < 10) { sout += (char)(cums[i] + '0'); }
                else { sout += (char)(cums[i] + 'A'-10); }
            }
            if (sout.Length()==0) { sout = "0"; } //input was zero, return 0
            //return the converted string

            delete fs;
            delete ts;
            delete cums;

            return sout;

}

void __fastcall Tpcfrm::EditB1KeyPress(TObject *Sender, char &Key)
{
  if (Key < '0' | Key > '9') Key = 0;        
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button2Click(TObject *Sender)
{
   EditB1->Text = "2";
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button3Click(TObject *Sender)
{
  EditB1->Text = "8";        
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button4Click(TObject *Sender)
{
  EditB1->Text = "10";        
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button5Click(TObject *Sender)
{
  EditB1->Text = "16";        
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button6Click(TObject *Sender)
{
  EditB2->Text = "2";
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button7Click(TObject *Sender)
{
  EditB2->Text = "8";
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button8Click(TObject *Sender)
{
  EditB2->Text = "10";
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button9Click(TObject *Sender)
{
  EditB2->Text = "16";
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button10Click(TObject *Sender)
{
  EditB3->Text = "2";
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button11Click(TObject *Sender)
{
  EditB3->Text = "8";        
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button12Click(TObject *Sender)
{
  EditB3->Text = "10";        
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Button13Click(TObject *Sender)
{
  EditB3->Text = "16";        
}
//---------------------------------------------------------------------------




void __fastcall Tpcfrm::Edit1Change(TObject *Sender)
{
  Button1Click(this);
  LabelL1->Visible = (Edit1->Text.Length()>42);
  LabelL2->Visible = (Label2->Caption.Length()>42);
  LabelL3->Visible = (Label3->Caption.Length()>42);
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Edit1KeyPress(TObject *Sender, char &Key)
{
  int i,x=100;

  if (Key == 27) Edit1->Text = "0";
  else if (Key=='\x01') Edit1->SelectAll();
  else if ((Key!='\b') & (Key!='\x03') & (Key!='\x16') & (Key!='\x18'))
  {
    Key = toupper(Key);
    i = StrToInt(EditB1->Text);
    if (Key >= '0' && Key <= '9') x = Key - '0';
      else if (Key >= 'A' && Key <= 'Z') x = 10+Key - 'A';

    if (x>=i) Key=0;
  }

}
//---------------------------------------------------------------------------



void __fastcall Tpcfrm::EditB1Change(TObject *Sender)
{
  int i,j;
  AnsiString s;
  bool err;

  j = StrToInt(EditB1->Text);
  i = StrToInt(bs1);
  s = Convert(i, j, cs1, &err);

  Edit1->Text = s;
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::EditB2Change(TObject *Sender)
{
  int i,j,n;
  AnsiString s,s1,s2;
  bool err;

  j = StrToInt(EditB2->Text);
  i = StrToInt(bs2);
  s = Convert(i, j, cs2, &err);

  if (!err) s = makeitfancy(j, s);
  Label2->Caption = s;


//  Edit2->Text = s;

}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::EditB3Change(TObject *Sender)
{
  int i,j,n;
  AnsiString s,s1,s2;
  bool err;

  j = StrToInt(EditB3->Text);
  i = StrToInt(bs3);
  s = Convert(i, j, cs3, &err);


  if (!err) s = makeitfancy(j, s);
  Label3->Caption = s;

}
//---------------------------------------------------------------------------


void Tpcfrm::INI_write(void)
{
TIniFile *ini;

  ini = new TIniFile( "PCALC10.INI" );

  ini->WriteBool( "Options", "Extended", Extended1->Checked);
  ini->WriteBool( "Options", "Separators",SeparatorsCB->Checked);
  ini->WriteBool( "Options", "Wheelrolover",WheelRollover1->Checked);
  ini->WriteInteger( "Options", "Notation",notate);
  ini->WriteInteger( "Options", "SignPosition",SignPos);

  ini->WriteString( "Values", "input",Edit1->Text);
  ini->WriteString( "Values", "Base1",EditB1->Text);
  ini->WriteString( "Values", "Base2",EditB2->Text);
  ini->WriteString( "Values", "Base3",EditB3->Text);

  ini->WriteString( "Values", "CS1",cs1);
  ini->WriteString( "Values", "CS2",cs2);
  ini->WriteString( "Values", "CS3",cs3);

  ini->WriteString( "Values", "BS1",bs1);
  ini->WriteString( "Values", "BS2",bs2);
  ini->WriteString( "Values", "BS3",bs3);


  delete ini;
}

void Tpcfrm::INI_read(void)
{
TIniFile *ini;

  ini = new TIniFile( "PCALC10.INI" );

  Extended1->Checked = ini->ReadBool( "Options", "Extended", false); //
  SeparatorsCB->Checked = ini->ReadBool( "Options", "Separators", true);
  WheelRollover1->Checked = ini->ReadBool( "Options", "Wheelrolover",false);
  notate = ini->ReadInteger( "Options", "Notation",1);
  SignPos = ini->ReadInteger( "Options", "SignPosition",1);

  Edit1->Text = ini->ReadString( "Values", "input","0");
  EditB1->Text = ini->ReadString( "Values", "Base1","16");
  EditB2->Text = ini->ReadString( "Values", "Base2","10");
  EditB3->Text = ini->ReadString( "Values", "Base3","2");

  cs1 = ini->ReadString( "Values", "CS1", "0");
  cs2 = ini->ReadString( "Values", "CS2", "0");
  cs3 = ini->ReadString( "Values", "CS3", "0");
  bs1 = ini->ReadString( "Values", "BS1", "16");
  bs2 = ini->ReadString( "Values", "BS2", "10");
  bs3 = ini->ReadString( "Values", "BS3", "2");


  Extended1Click(NULL);
  Button1Click(this);
  delete ini;
}




void __fastcall Tpcfrm::FormCreate(TObject *Sender)
{

  notate = 1; // U8
  not_n = 0;
  SignPos = 0;  // MSB
  Height = 134;
  INI_read();

  StatusBar1->Panels->Items[1]->Text = "Programmer's converter V1.01. (c)2007 Mare";

}
//---------------------------------------------------------------------------

char decrement (char ch, int base){

char x;

    if (ch >= '0' && ch <= '9') x = ch - '0';
      else if (ch >= 'A' && ch <= 'Z') x = 10+ch - 'A';

    if (x>0) x--; else if (pcfrm->WheelRollover1->Checked) x = base-1;


    if (x<=9) x = x+'0';
      else if (x>9) x = x-10 + 'A';

    return x;
}

char increment (char ch, int base){

char x;

    if (ch >= '0' && ch <= '9') x = ch - '0';
      else if (ch >= 'A' && ch <= 'Z') x = 10+ch - 'A';

    if (x<base-1) x++; else if (pcfrm->WheelRollover1->Checked) x = 0;

    if (x<=9) x = x+'0';
      else if (x>9) x = x-10 + 'A';

    return x;
}


//---------------------------------------------------------------------------
void __fastcall Tpcfrm::FormMouseWheelDown(TObject *Sender,
      TShiftState Shift, TPoint &MousePos, bool &Handled)
{

int px, py,n=-1,i;
char ch;
AnsiString s;

   px = MousePos.x - Edit1->Left - Left - 7;
   py = MousePos.y - Edit1->Top - Top;

   if ((py>=43) && (py<=(65)))
   {
     if ((px%11)<=8) n = px/11;
     if (n>=0) {
        i = StrToInt(bs1);
        s = Edit1->Text;
        ch = s.c_str()[n];
        ch = decrement (ch, i);
        s.c_str()[n] = ch;
        Edit1->Text = s;
     }
   }

   // 7  18  29  40
   //  15  26  37
   Handled = true;
}
//---------------------------------------------------------------------------


void __fastcall Tpcfrm::FormMouseWheelUp(TObject *Sender,
      TShiftState Shift, TPoint &MousePos, bool &Handled)
{

int px, py,n=-1,i;
char ch;
AnsiString s;

   px = MousePos.x - Edit1->Left - Left - 7;
   py = MousePos.y - Edit1->Top - Top;

   if ((py>=43) && (py<=(65)))
   {
     if ((px%11)<=8) n = px/11;
     if (n>=0) {
        i = StrToInt(bs1);
        s = Edit1->Text;
        ch = s.c_str()[n];
        ch = increment (ch, i);
        s.c_str()[n] = ch;
        Edit1->Text = s;
     }
   }

   // 7  18  29  40
   //  15  26  37
   Handled = true;
}
//---------------------------------------------------------------------------



void __fastcall Tpcfrm::About1Click(TObject *Sender)
{
  FormAbout->ShowModal();         
}
//---------------------------------------------------------------------------



void __fastcall Tpcfrm::Extended1Click(TObject *Sender)
{
  Notation1->Enabled = Extended1->Checked;
  StatusBar1->Visible = Extended1->Checked;
  if (Extended1->Checked)
  {
     Height = 271;
  } else
  {
     Height = 134;
  }
  INI_write();
}
//---------------------------------------------------------------------------


void __fastcall Tpcfrm::WheelRollover1Click(TObject *Sender)
{
    INI_write();
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::S81Click(TObject *Sender)
{
  notate = 0; UpdateNotation();
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::U81Click(TObject *Sender)
{
  notate = 1; UpdateNotation();
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::S161Click(TObject *Sender)
{
  notate = 2; UpdateNotation();

}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::U161Click(TObject *Sender)
{
  notate = 3; UpdateNotation();

}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::S321Click(TObject *Sender)
{
  notate = 4; UpdateNotation();

}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::U321Click(TObject *Sender)
{
  notate = 5; UpdateNotation();

}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::S641Click(TObject *Sender)
{
  notate = 6; UpdateNotation();

}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::U641Click(TObject *Sender)
{
  notate = 7; UpdateNotation();

}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::NotatCustom1Click(TObject *Sender)
{
   notate = 8; UpdateNotation();
}
//---------------------------------------------------------------------------

void Tpcfrm::UpdateNotation(void){

int i,j,l;
char prenos;
AnsiString s,s1;
bool err;

   S81->Checked = false;
   U81->Checked = false;
   S161->Checked = false;
   U161->Checked = false;
   S321->Checked = false;
   U321->Checked = false;
   S641->Checked = false;
   U641->Checked = false;
   NotatCustom1->Checked = false;

   switch (notate) {
     case 0 : {  S81->Checked = true;  LabelNot->Caption = "S8";  break; }
     case 1 : {  U81->Checked = true;  LabelNot->Caption = "U8";  break; }
     case 2 : {  S161->Checked = true; LabelNot->Caption = "S16";   break; }
     case 3 : {  U161->Checked = true; LabelNot->Caption = "U16";   break; }
     case 4 : {  S321->Checked = true; LabelNot->Caption = "S32";   break; }
     case 5 : {  U321->Checked = true; LabelNot->Caption = "U32";  break; }
     case 6 : {  S641->Checked = true; LabelNot->Caption = "S64";   break; }
     case 7 : {  U641->Checked = true; LabelNot->Caption = "U64";  break; }
     case 8 : {  NotatCustom1->Checked = true; LabelNot->Caption = "CUST";  break; }
   }

  i = StrToInt(EditB1->Text);
  s = Convert(i, 2, Edit1->Text, &err);

  if (s.Length()==1)
    StatusBar1->Panels->Items[0]->Text = IntToStr(s.Length())+"bit.";
  else
    StatusBar1->Panels->Items[0]->Text = IntToStr(s.Length())+"bits.";
  if (!err)
  {
     if ((notate == 0) | (notate == 1)) bitwidth=8;                  
     if ((notate == 2) | (notate == 3)) bitwidth=16;
     if ((notate == 4) | (notate == 5)) bitwidth=32;
     if ((notate == 6) | (notate == 7)) bitwidth=64;
     s.Length();
     if (s.Length()>bitwidth) Label4->Caption = "Number is too large (for about "
                       +IntToStr(s.Length()-bitwidth)+" bits)." ;
     else {
       if ((notate == 1) | (notate == 3) | (notate == 5) | (notate == 7)) {  // unsigned
         Label1->PopupMenu = NULL;
         LabelBitmap->Visible = false;
         Label4->Caption = Convert(i, 10, Edit1->Text, &err);
       } else {  // signed  notation
         Label1->PopupMenu = PopupMenuNotationBitmap;
         s1 = "";
         for (j=0; j<bitwidth; j++) s1 = s1 + "_";
         s1.c_str()[SignPos]='S';
         LabelBitmap->Caption = s1;
         LabelBitmap->Visible = true;
         s1 = "";
         for (j=0; j<bitwidth-s.Length(); j++) s1 = s1 + "0";
         s1 = s1 + s;
         if (s1.c_str()[SignPos] == '0') Label4->Caption = Convert(2, 10, s, &err);  // Sign bit = 0
         else // Sign bit = 1
         {
           s1 = "";
           if (SignPos == 0) {
             for (j=1; j<bitwidth; j++) if (s.c_str()[j] == '1') s1 = s1 + "0"; else s1=s1+"1";
             s1 = Convert(2, 10, s1, &err);
             prenos = 0;
             s1.c_str()[s1.Length()-1] = s1.c_str()[s1.Length()-1] + 1;
             if (s1.c_str()[s1.Length()-1] > '9') prenos = s1.c_str()[s1.Length()-1]-'9';   else prenos = 0;
             for (j=s1.Length()-2; j>=0; j--) {  // add one
               s1.c_str()[j] = s1.c_str()[j] + prenos;
               if (s1.c_str()[j] > '9') prenos = s1.c_str()[j]-'9';   else prenos = 0;
             }
             Label4->Caption = "-"+s1;
           }
         }
       }
       s1 = "";
       for (j=0; j<bitwidth-s.Length(); j++) s1 = s1 + "0";
       s1 = s1 + s;
       Label1->Caption = s1;
     }
  }


}

//---------------------------------------------------------------------------
void __fastcall Tpcfrm::LabelL1Click(TObject *Sender)
{
  ShowMessage(Edit1->Text);
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::LabelL2Click(TObject *Sender)
{
  ShowMessage(Label2->Caption);
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::LabelL3Click(TObject *Sender)
{
  ShowMessage(Label3->Caption);
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Copy1Click(TObject *Sender)
{
TComponent *pComponent = EditPopupMenu->PopupComponent;
  if (pComponent)
  {
    switch (pComponent->Tag) {
      case 1 : Clipboard()->SetTextBuf(Label2->Caption.c_str()); break;
      case 2 : Clipboard()->SetTextBuf(Label3->Caption.c_str()); break;
      case 3 : Clipboard()->SetTextBuf(Label4->Caption.c_str()); break;
    }
  }

}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::Csource1Click(TObject *Sender)
{
TComponent *pComponent = EditPopupMenu->PopupComponent;
AnsiString s;
int base;

  if (pComponent)
  {
    switch (pComponent->Tag) {
      case 1 : s=Label2->Caption; base = StrToInt(EditB2->Text); break;
      case 2 : s=Label3->Caption; base = StrToInt(EditB3->Text); break;
      case 3 : s=Label4->Caption; base = 10; break;
    }
    if (pComponent->Tag == 3)
    {
       switch (notate) {
         case 0 : {  s = "signed char a = "+s+";";  break; }
         case 1 : {  s = "unsigned char a = "+s+";";  break; }
         case 2 : {  s = "signed short int a = "+s+";";   break; }
         case 3 : {  s = "unsigned short int a = "+s+";";   break; }
         case 4 : {  s = "signed int a = "+s+";";   break; }
         case 5 : {  s = "unsigned int a = "+s+";";  break; }
         case 6 : {  s = "signed __int64 a = "+s+";";   break; }
         case 7 : {  s = "unsigned __int64 a = "+s+";";  break; }
       }

    }
    else 
    if (base == 10) s = "a = "+s+";"; else
      if (base == 16) s = "b = 0x"+s+";"; else
        s="//* Not valid number base. Base = "+IntToStr(base)+", Value = "+s+" *//";

    Clipboard()->SetTextBuf(s.c_str());

  }
                   
}
//---------------------------------------------------------------------------

void __fastcall Tpcfrm::SetSignbit1Click(TObject *Sender)
{
  TComponent *pComponent = PopupMenuNotationBitmap->PopupComponent;

  if ((not_n == 0) | (not_n == bitwidth-1))   SignPos = not_n;
  else ShowMessage("Invalid sign bit position!");

  UpdateNotation();

}
//---------------------------------------------------------------------------


void __fastcall Tpcfrm::Label1MouseMove(TObject *Sender, TShiftState Shift,
      int X, int Y)
{

int n;

    if ((Y>=5) && (Y<=14)) {
      n = X % 11;
      if ((n>=2) && (n<=9)) not_n = X / 11; else not_n = -1;

      if (not_n>bitwidth-1) not_n = -1;
    }

}
//---------------------------------------------------------------------------
      //2 9    13 19     24 30

